package com.googlecode.gwt.test.plugin.resourcesListner;


import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;

import com.googlecode.gwt.test.plugin.editors.GWTCsvConfiguration;
import com.googlecode.gwt.test.plugin.editors.GWTCsvScanner;
import com.googlecode.gwt.test.plugin.editors.KeyWords;
import com.googlecode.gwt.test.plugin.util.GwtCsvTestType;
import com.googlecode.gwt.test.plugin.util.MacroExtractor;

public class MacroChangeListner implements IResourceChangeListener {

	KeyWords keyWords;

	private IProject currentProject;

	private List<String> detectedKeyWords;

	GWTCsvConfiguration configuration;

	private IResourceDeltaVisitor macroVisitor;

	private IType[] typeHierarchy = null;

	public MacroChangeListner(KeyWords keyWords, IProject currentProject, GWTCsvConfiguration configuration) {
		super();
		this.currentProject = currentProject;
		this.keyWords = keyWords;
		this.detectedKeyWords = new ArrayList<String>();
		this.configuration = configuration;
		this.macroVisitor = new IResourceDeltaVisitor() {
			public boolean visit(IResourceDelta delta) {
				IResource res = delta.getResource();
				switch (delta.getKind()) {
				case IResourceDelta.ADDED:
				case IResourceDelta.CHANGED:
					if (res.getType() == IResource.FILE && "csv".equals(res.getFileExtension())
							&& isInRelativePath(res.getProjectRelativePath().toString()))
						compute();
					break;
				}
				return true; // visit the children
			}
		};
		analyse();
	}

	private void analyse() {
		detectedKeyWords.clear();
		customHierarchy();
		keyWords.setCUSTOM_MACROS(detectedKeyWords);
		GWTCsvScanner sc = (GWTCsvScanner) configuration.getScanner();
		sc.update();
	}

	private void customHierarchy() {
		try {

			IType[] results = getHierarchy();
			for (IType type : results) {
				process(type);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean isInRelativePath(String string) {
		for (String relativePath : getRelativePath()) {
			if (string.contains(relativePath))
				return true;
		}
		return false;
	}

	private List<String> getRelativePath() {
		List<String> relativePath = new ArrayList<String>();
		try {

			IType[] types = getHierarchy();
			for (IType t : types) {
				String lpath = getValueAnnotation(t);
				if (lpath != null)
					relativePath.add(lpath);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
		return relativePath;
	}

	private IType[] getHierarchy() throws JavaModelException {
		if (typeHierarchy == null) {
			IType clazz = GwtCsvTestType.getGwtCsvTestType(currentProject);
			ITypeHierarchy hierarchy = clazz.newTypeHierarchy(null);
			typeHierarchy = hierarchy.getAllSubtypes(clazz);
		}
		return typeHierarchy;
	}

	private void process(IType t) {
		String path = null;
		path = getValueAnnotation(t);
		if (path != null) {
			MacroExtractor extractor = new MacroExtractor(findRootDirProjects(path));
			detectedKeyWords.addAll(extractor.extractMacros(currentProject));
		}
	}

	private String getValueAnnotation(IType t) {
		try {
			for (IAnnotation annotation : t.getAnnotations()) {
				if (annotation.getElementName().contains("CsvMacros")) {
					for (IMemberValuePair pair : annotation.getMemberValuePairs()) {
						if (pair.getMemberName().equals("value"))
							return (String) pair.getValue();
					}
				}
			}
		} catch (JavaModelException e) {
		}
		return null;
	}

	@Override
	public void resourceChanged(IResourceChangeEvent event) {
		if (event.getType() != IResourceChangeEvent.POST_CHANGE) {
			return;
		}
		try {
			event.getDelta().accept(macroVisitor);
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	public void compute() {
		analyse();
		GWTCsvScanner sc = (GWTCsvScanner) configuration.getScanner();
		sc.update();
	}

	private File findRootDirProjects(String path) {
		IResource retour = null;
		retour = currentProject.findMember(path);
		return retour == null ? null : retour.getLocation().toFile();
	}

	public void clean() {
		this.typeHierarchy = null;
		this.compute();
	}

}
